/**
 * @author Clark
 * @email haixuanwoTxh@gmail.com
 * @date 2021-10-23
 */

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
#include <QKeyEvent>
#include "talkprotocol.h"
#include <QDesktopWidget>
#include <QGuiApplication>
#include <iostream>

#define ONLINE "online"
#define OFFLINE "offline"

/**
 * @brief 回应码
 */
enum{
    OK = 200,
    FAIL = 404
};

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    loginStatus = UNLOGGED;

    // main window
    this->setWindowTitle("letsTalk");
    this->move(QApplication::desktop()->screenGeometry().center() - this->rect().center());
    this->setFixedSize(this->width(), this->height());

    // set connect status show color
    ui->connectStatus->setStyleSheet("QLabel { background-color : red; color : black; }");

    // logup window
    logupUi = new logupwindow;
    logupUi->setWindowTitle("logup");
    logupUi->move(QApplication::desktop()->screenGeometry().center() - logupUi->rect().center());
    logupUi->setFixedSize(logupUi->width(), logupUi->height());

    connect(logupUi, &logupwindow::go_closed, this, &MainWindow::show_self);
    connect(logupUi, &logupwindow::logup_new_user, this, &MainWindow::send_logup_message);

    // talk window
    talkUi = new talkwindow;
    talkUi->setWindowTitle("talk");
    talkUi->move(QApplication::desktop()->screenGeometry().center() - talkUi->rect().center());
    talkUi->setFixedSize(talkUi->width(), talkUi->height());

    connect(talkUi, &talkwindow::go_closed, this, &MainWindow::show_self);
    connect(talkUi, &talkwindow::send_talk_msg, this, &MainWindow::send_talk_msg);

    // server connect
    tcpSocket = new QTcpSocket(this);
    connect(tcpSocket, SIGNAL(connected()), this, SLOT(tcp_connected_callback()));
    connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(tcp_disconnected_callback()));
    connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(data_received_callback()));
    connect_server();
}

MainWindow::~MainWindow()
{
    delete ui;
}

bool MainWindow::connect_server()
{
    tcpSocket->close();
    QString ipAddress = ui->lineEditIP->text();
    int port = ui->lineEditPort->text().toInt();
    tcpSocket->connectToHost(ipAddress, port);
}

void MainWindow::show_self()
{
    this->show();
}

void MainWindow::send_logup_message(QString user, QString passwd)
{
    int len = 0;
    int result = 0;
    int packetLen = 0;
    unsigned char buf[128] = {0};
    unsigned char packet[1024] = {0};

    tp.set_user_passwd(user, passwd, buf, sizeof(buf), len);

    result = tp.build_protocol_packet(LOGUP, buf, len, packet, sizeof(packet), packetLen);
    if (result)
    {
        tcpSocket->write((char *)packet, packetLen);
    }
}

void MainWindow::send_talk_msg(uint32_t userID, QString sendStr)
{
    unsigned short len = 0;
    int result = 0;
    int packetLen = 0;
    int index = 0;
    unsigned char buf[1024] = {0};
    unsigned char packet[1024] = {0};

    tp.set_value_to_4Byte_buf(userID, buf);
    index += 4;

    len = sendStr.length();
    tp.set_value_to_2Byte_buf(len, buf+index);
    index += 2;

    memcpy(buf+index, sendStr.toLatin1().data(), len);
    index += len;


    //    QString str = ui->textEditWrite->toPlainText();
    //    tcpSocket->write(str.toUtf8().data());

    result = tp.build_protocol_packet(MESSAGE, buf, index, packet, sizeof(packet), packetLen);
    if (result)
    {
        tcpSocket->write((char *)packet, packetLen);
    }
}

void MainWindow::tcp_connected_callback()
{
    ui->connectStatus->setText(ONLINE);
    ui->connectStatus->setStyleSheet("QLabel { background-color : green; color : black; }");
}

void MainWindow::tcp_disconnected_callback()
{
    loginStatus = UNLOGGED;
    ui->connectStatus->setText(OFFLINE);
    ui->connectStatus->setStyleSheet("QLabel { background-color : red; color : black; }");
}

void MainWindow::data_received_callback()
{
    int index = 0;
    bool result = false;
    qint64 readLen = 0;
//    QByteArray datagram;
    Action action;

#define BUF_MAX_SIZE 4096
    static char *buf = nullptr;
    if (nullptr == buf)
    {
        buf = (char *)malloc(BUF_MAX_SIZE);
    }
    static int bufDataLen = 0;
    int packetStart = 0;
    int packetLen = 0;

    unsigned char data[4096] = {0};
    int dataLen = 0;

    while(tcpSocket->bytesAvailable() > 0)
    {
//        datagram.resize(tcpSocket->bytesAvailable());
        readLen = tcpSocket->read(buf + bufDataLen, BUF_MAX_SIZE - bufDataLen);
        if (readLen <= 0)
        {
            qDebug()<<"Can't read data from socket";
            break;
        }

        bufDataLen += readLen;

        tp.print_data_as_hex((unsigned char*)buf, bufDataLen);

GET:
        int result = tp.get_complete_protocol_packet((unsigned char*)buf, bufDataLen, packetStart, packetLen);
        if (-1 == result)       // buf中未处理的数据不够最小包长了
        {
            memmove(buf, buf + bufDataLen - BASIC_PACKET_LEN +1, BASIC_PACKET_LEN - 1);
            bufDataLen = BASIC_PACKET_LEN - 1;
            qDebug()<<"can't find head";
            continue;
        }
        else if (2 == result)   // buf中当前数据不够做判断，需继续读取
        {
            qDebug()<<"need more data";
            continue;
        }
        else if (1 == result)   // buf中存在不完整的包，若包数据前有无效数据则覆盖
        {
            qDebug()<<"get incomplete packet: "<<packetStart;
            if (packetStart > 0)
            {
                bufDataLen -= packetStart;
                memmove(buf, buf + packetStart, bufDataLen);
            }
            continue;
        }
        else if (0 == result)   // buf获取到完整的数据包，解析
        {
            qDebug()<<"get a complete packet";
            tp.print_data_as_hex((unsigned char*)buf + packetStart, packetLen);

            result = tp.parse_protocol_packet((unsigned char*)buf + packetStart, packetLen, action, data, sizeof(data), dataLen);
            if (false == result)
            {
                qDebug()<<"parse packet error";
                break;
            }

            switch(action)
            {
                case LOGUP:
                {
                    if (2 == dataLen)
                    {
                        unsigned short res = 0;
                        tp.get_value_from_2Byte_buf(data, res);
                        if (OK == res)
                        {
                            qDebug()<<"logup success";
                        }
                    }
                    break;
                }
                case LOGIN:
                {
                    qDebug()<<"login dataLen:"<<dataLen;
                    if (2 == dataLen)
                    {
                        unsigned short res = 0;
                        tp.get_value_from_2Byte_buf(data, res);
                        qDebug()<<"login res"<<res;
                        if (OK == res)
                        {
                            loginStatus = LOGGED;
                            qDebug()<<"login success";
                            talkUi->show();
                            this->hide();
                        }
                    }
                    break;
                }
                case LOGOUT:
                {
                    loginStatus = UNLOGGED;
                    break;
                }
                case MESSAGE:
                {
                    index = 0;
                    u_int32_t ID = 0;
                    u_int16_t messageLen = 0;

                    tp.get_value_from_4Byte_buf(data, ID);
                    index +=4;

                    tp.get_value_from_2Byte_buf(data + index, messageLen);
                    index +=2;

                    char buf[1024] ={0};
                    memcpy(buf, data + index, messageLen);

                    QString recvMessage(buf);
                    talkUi->append_show_recv_message(ID, recvMessage);
                    qDebug()<<"recvmessage: "<<recvMessage;
                    break;
                }
                case PUBLIC_CLIENT_INFO:
                {
                    index = 0;
                    unsigned int ID = 0;
                    unsigned int clientNum = 0;
                    unsigned short userLen = 0;
                    char buf[256] = {0};
                    QString user;
                    QVector<QPair<unsigned int, QString>> ID_user;

                    tp.get_value_from_4Byte_buf(data, clientNum);
                    index +=4;

                    for (unsigned int i = 0; i < clientNum; i++)
                    {
                        tp.get_value_from_4Byte_buf(data + index, ID);
                        index += 4;

                        tp.get_value_from_2Byte_buf(data + index, userLen);
                        index +=2;

                        memset(buf, 0, sizeof(buf));
                        memcpy(buf, data + index, userLen);
                        user = buf;
                        index += userLen;

                        ID_user.push_back(qMakePair(ID, user));
                    }

                    talkUi->flush_client_info(ID_user);
                }
                default:
                    break;
            }

            // buf还存在多余的数据则再次获取里面是否还有完整包
            int remainDataLen = bufDataLen - packetStart - packetLen;
            if (remainDataLen > 0)
            {
                bufDataLen = remainDataLen;
                memmove(buf, buf + packetStart + packetLen, remainDataLen);
                qDebug()<<"remainDataLen: "<<remainDataLen;
                goto GET;
            }
            else
            {
                bufDataLen = 0;
            }
        }
    }
}

#if 0
void MainWindow::on_pushButtonSend_clicked()
{
    if(ui->lineEditSend->text().isEmpty())
    {
        return;
    }

    QString message = userName + tr(":") + ui->lineEditSend->text();
    tcpSocket->write(message.toLatin1(), message.length());
    ui->lineEditSend->clear();

    // 发送数据显示在聊天框
    ui->listWidgetTalk->addItem(message);
    ui->listWidgetTalk->scrollToBottom();
}

//键盘事件
void MainWindow::keyPressEvent(QKeyEvent *event)
{
    switch(event->key())
    {
        case Qt::Key_Enter:
            on_pushButtonSend_clicked();
            qDebug()<<"run slot func";
            break;

        case Qt::Key_Return: //16777220:
            on_pushButtonSend_clicked();
            qDebug()<<"shit ni ma bi";
            break;

        default:
            qDebug()<<"what's this:"<<event->key();
            break;
    }
}
#endif

void MainWindow::on_pushButtonReconnect_clicked()
{
    connect_server();
}

void MainWindow::on_pushButtonLogup_clicked()
{
    this->hide();
    logupUi->show();
}

void MainWindow::on_pushButtonLogin_clicked()
{
    int len = 0;
    int result = 0;
    int packetLen = 0;
    unsigned char buf[128] = {0};
    unsigned char packet[1024] = {0};

    QString user = ui->lineEditUser->text();
    QString passwd = ui->lineEditPasswd->text();

    tp.set_user_passwd(user, passwd, buf, sizeof(buf), len);
    tp.print_data_as_hex(buf, len);

    result = tp.build_protocol_packet(LOGIN, buf, len, packet, sizeof(packet), packetLen);
    if (result)
    {
        tcpSocket->write((char *)packet, packetLen);
    }
}
